﻿// Autore: Alessio Parma

using System;
using System.Collections;
using System.Collections.Generic;
using System.Data.Objects;
using System.Data.Objects.DataClasses;
using System.Linq;

namespace RecipesManager.Data.Implementation
{
    abstract class BaseEntities<TClass, TInterface, TCollection> : IEntities<TInterface>
        where TClass : class, TInterface
        where TInterface : class, IEntity
        where TCollection : IEnumerable<TClass>
    {
        protected readonly Func<TCollection> Items;

        protected BaseEntities(Func<TCollection> items)
        {
            Items = items;
        }

        #region IEntities<TInterface> Members

        public int Count
        {
            get { return this.Count(); }
        }

        public TInterface LastAdded { get; protected set; }

        public IEnumerable<TInterface> Unordered
        {
            get { return Items(); }
        }

        public TInterface this[int id]
        {
            get { return GetEntity(id); }
        }

        public bool Contains(int id)
        {
            return GetEntity(id) != null;
        }

        public virtual IEnumerator<TInterface> GetEnumerator()
        {
            return Items().GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        public abstract void Remove(int id);

        #endregion

        protected TClass GetEntity(int id)
        {
            return (TClass) this.FirstOrDefault(i => i.Id == id);
        }
    }

    abstract class BaseNamedEntities<TClass, TInterface, TCollection> : BaseEntities<TClass, TInterface, TCollection>,
                                                                        INamedEntities<TInterface>
        where TClass : class, TInterface
        where TInterface : class, INamedEntity
        where TCollection : IEnumerable<TClass>
    {
        protected BaseNamedEntities(Func<TCollection> items) : base(items)
        {
        }

        #region INamedEntities<TInterface> Members

        public TInterface this[string name]
        {
            get { return GetEntity(name); }
        }

        public bool Contains(string name)
        {
            return GetEntity(name) != null;
        }

        public override IEnumerator<TInterface> GetEnumerator()
        {
            return Items().OrderBy(e => e.Name).GetEnumerator();
        }

        public abstract void Remove(string name);

        #endregion

        protected TClass GetEntity(string name)
        {
            name = name.ToKey();
            return Items().FirstOrDefault(i => i.Name == name);
        }
    }

    abstract class Objects<T, TI> : BaseEntities<T, TI, ObjectSet<T>>
        where T : EntityObject, TI
        where TI : class, IEntity
    {
        protected Objects(Func<ObjectSet<T>> items) : base(items)
        {
        }

        public override void Remove(int id)
        {
            Items().DeleteObject(GetEntity(id));
            Manager.SaveChanges();
        }

        protected T Add(T item)
        {
            Items().AddObject(item);
            Manager.SaveChanges();
            LastAdded = item;
            return item;
        }
    }

    abstract class NamedObjects<T, TI> : BaseNamedEntities<T, TI, ObjectSet<T>>
        where T : EntityObject, TI
        where TI : class, INamedEntity
    {
        protected NamedObjects(Func<ObjectSet<T>> items) : base(items)
        {
        }

        public override void Remove(int id)
        {
            Remove(GetEntity(id));
        }

        public override void Remove(string name)
        {
            Remove(GetEntity(name));
        }

        protected T Add(T item)
        {
            Items().AddObject(item);
            Manager.SaveChanges();
            LastAdded = item;
            return item;
        }

        protected void Remove(T item)
        {
            Items().DeleteObject(item);
            Manager.SaveChanges();
        }
    }

    abstract class NamedEntities<T, TI> : BaseNamedEntities<T, TI, EntityCollection<T>>
        where T : EntityObject, TI
        where TI : class, INamedEntity
    {
        readonly Func<ObjectSet<T>> _set;

        protected NamedEntities(Func<EntityCollection<T>> items, Func<ObjectSet<T>> set)
            : base(items)
        {
            _set = set;
        }

        protected TI AddName(string name)
        {
            name = name.ToKey();
            var item = _set().First(i => i.Name == name);
            Items().Add(item);
            Manager.SaveChanges();
            LastAdded = item;
            return item;
        }

        public override void Remove(int id)
        {
            Items().Remove(GetEntity(id));
            Manager.SaveChanges();
        }

        public override void Remove(string name)
        {
            Items().Remove(GetEntity(name));
            Manager.SaveChanges();
        }
    }

    abstract class LinkSet<T, TI> : BaseEntities<T, TI, EntityCollection<T>>
        where T : EntityObject, TI
        where TI : class, IEntity
    {
        protected readonly Func<int> IdGetter;

        protected LinkSet(Func<EntityCollection<T>> items, Func<int> idGetter)
            : base(items)
        {
            IdGetter = idGetter;
        }

        public override void Remove(int id)
        {
            Items().Remove(GetEntity(id));
            Manager.SaveChanges();
        }

        protected T Add(T item)
        {
            Items().Add(item);
            Manager.SaveChanges();
            LastAdded = item;
            return item;
        }
    }

    abstract class NamedLinkSet<T, TI> : BaseNamedEntities<T, TI, EntityCollection<T>>
        where T : EntityObject, TI
        where TI : class, INamedEntity
    {
        protected readonly Func<int> IdGetter;

        protected NamedLinkSet(Func<EntityCollection<T>> items, Func<int> idGetter)
            : base(items)
        {
            IdGetter = idGetter;
        }

        public override sealed void Remove(int id)
        {
            Items().Remove(GetEntity(id));
            Manager.SaveChanges();
        }

        public override sealed void Remove(string name)
        {
            Items().Remove(GetEntity(name));
            Manager.SaveChanges();
        }

        protected T Add(T item)
        {
            Items().Add(item);
            Manager.SaveChanges();
            LastAdded = item;
            return item;
        }
    }
}